\bibliographystyle{gbt7714-2005}

\chapter{CodeIgnite框架}

\epigraphhead[60]{\epigraph{健壮的结构远比精巧的设计来得重要。换句话说，结构是第一位的，功能是第二位的。}{\textit{Eric Raymond《大教堂与集市》}}}

在PHP开发中，选择合适的框架有助于加快软件开发，节约宝贵的项目时间，让开发者专注于功能的实现上。由于流行的框架经过了大量项目的检验，所以使用框架还有助于创建更加稳定和安全的应用程序。另外，使用框架还能提高开发人员编程的规范性，有助于团队成员的协作。

在PHP开发社区中，有着丰富的开发框架供开发人员选择。据\href{http://www.sitepoint.com/best-php-frameworks-2014/}{Sitepoint网站的调查}显示，最流行的PHP框架前五名为：Laravel、Phalcon、Symfony2、CodeIgnite和yii。

\section{CodeIgnite框架介绍}
\href{http://codeigniter.org.cn/}{CodeIgnite}是一套给 PHP 网站开发者使用的应用程序开发框架和工具包。它提供一套丰富的标准库以及简单的接口和逻辑结构，其目的是使开发人员更快速地进行项目开发。使用 CodeIgniter可以减少代码的编写量，并将你的精力投入到项目的创造性开发上。\cite{EllislabInc，2014}

CodeIgnite框架具有易学、轻量、扩展性强的优点，另外，CodeIgnite框架不但中文文档非常齐全，网络上还有不少高质量的视频教程， 这些因素都使得该框架的学习成本大大降低。

CodeIgnite框架使用Apache/BSD-style开源许可协议，用户可以免费使用该框架。

\subsection{服务器要求}
PHP 版本 5.2.4 或更新的版本，推荐5.4。支持的数据库为：MySQL (4.1+), MySQLi, MS SQL, Postgres, Oracle, SQLite 和 ODBC。

\subsection{CodeIgnite中的MVC}
CodeIgniter 是基于模型-视图-控制器这一设计模式的。

\begin{description}
  \item [模型] 代表数据结构。通常来说，模型类将包含取出、插入、更新数据库资料等等功能。
  \item [视图] 是展示给用户的信息。一个视图通常是一个网页，但是在 CodeIgniter 中，一个视图也可以是一个页面片段，如页头、页尾。它还可以是一个 RSS 页面，或任何其它类型的“页面”。
  \item [控制器] 是模型、视图以及其他任何处理 HTTP 请求所必须的资源之间的中介，并生成网页。
\end{description}

CodeIgniter 在 MVC 使用上非常宽松，MVC模型不是必需的。

\subsection{CodeIgnite的工作流程}
图\ref{fig:appflow}说明数据流如何贯穿整个系统：
\begin{figure}[htbp]
	\centering
	\includegraphics[width=120mm]{raw/appflowchart.jpg}
	\caption{CodeIgnite流程图}
	\label{fig:appflow}
\end{figure}
\begin{enumerate}
\item index.php 作为前端控制器，初始化运行 CodeIgniter 所需要的基本资源。
\item Router 检查 HTTP 请求，以确定谁来处理请求。
\item 如果缓存(Cache)文件存在，它将绕过通常的系统执行顺序，被直接发送给浏览器。
\item 安全(Security)。应用程序控制器(Application Controller)装载之前，HTTP 请求和任何用户提交的数据将被过滤。
\item 控制器(Controller)装载模型、核心库、辅助函数，以及任何处理特定请求所需的其它资源。
\item 最终视图(View)渲染发送到 Web 浏览器中的内容。如果开启缓存(Caching)，视图首先被缓存，所以将可用于以后的请求。
\end{enumerate}

\section{CodeIgnite的下载与安装}

\subsection{下载}
可从\href{http://codeigniter.org.cn/user_guide/installation/downloads.html}{官方中文站点}下载最新版。截至目前，其最新版本为CodeIgniter V 3.0.0。

或者，我们还可以从\href{http://www.github.com}{Github站点}\footnote{Github网站是一个社交化代码托管网站，目前拥有用户600多万，在该网站，你可以非常方便的和朋友、同事、同学甚至陌生人分享代码或者共同维护代码。}下载。
\subsection{安装}
CodeIgniter 安装分为四个步骤：
\begin{enumerate}
	\item 解压缩安装包。
	\item 把 CodeIgniter 文件夹和里面的文件上传到你的服务器。如果是本地环境，直接解压到Web服务器的文档根目录，例如c:/wamp/www/。\footnote{先暂时删除application目录中的.htaccess文件，以保证application目录中文件的访问权限。}
	\item 用任何文本编辑器打开 application/config/config.php 去设置你的网站根 URL。
	\item 如果你打算使用数据库，用任何文本编辑器打开 application/config/database.php 去设置你的数据库参数。
\end{enumerate}

需要注意的是，后两步并非必须，只在有需要的时候进行。如果一切正常，在浏览器地址栏中输入安装后的目录，如http://localhost/ci，就能看到CodeIgnite框架默认的欢迎页面。

\subsection{目录结构}
解压之后，CodeIgnite的目录中有以下子目录和文件，分别是application、system、user\_guide和index.php。
\subsubsection{application}
实际应用目录。项目的所有源程序应该放在这个目录。Application目录默认情况下包含以下子目录：
\begin{quote}
	\begin{description}
		\item [cache] 存放缓存文件；
		\item [config] 包含网站基本配置的文件，如默认控制器、数据库连接信息等等；
		\item [controllers] MVC框架中的控制器都应存放于此；
		\item [core] 如果必要时，可对CodeIgnite核心代码的改写或扩充，这些扩展或复写的代码存放于此；
		\item [errors] 错误提示页面；
		\item [helpers] 自定义的辅助函数；
		\item [hooks] 存放为项目创建的“钩子”。钩子是用来装载其它文件的控制方法；
		\item [language] 存放不同语言的显示信息，如错误信息、验证信息可用不同语言来显示，默认为英语；
		\item [libraries] 类库文件，如果要对CodeIgnite类库进行扩展，则应放于此目录，默认为空；
		\item [logs] 日志文件存放目录；
		\item [models] MVC框架中的模型都应存放在此目录；
		\item [third\_party] 第三方类库存放目录；
		\item [views] MVC框架中的视图
	\end{description}
\end{quote}

\subsubsection{system}
CodeIgnite 框架核心程序，不建议对其进行修改或扩展，以保证将来版本升级时的平滑性。

\subsubsection{user\_guide}
CodeIgnite框架用户指南，只不过是英语版本，汉语版本请访问\href{http://codeigniter.org.cn/user_guide/toc.html}{CodeIgnite中文站点}。

\subsubsection{index.php}
通过图\ref{fig:appflow}我们能看到，CodeIgnite框架的index.php实际上是整个框架的入口文件，该文件负责初始化运行CodeIgniter。

\section{CodeIgnite框架的基本概念}
在下面的内容中，我们将介绍CodeIgnite框架的基本概念，并用简单的案例来演示该框架的使用。

\subsection{CodeIgniter URL}
默认情况下，CodeIgniter 中的 URL 使用分段表示的方法：

\begin{lstlisting}
example.com/index.php/class/function/ID
\end{lstlisting}

根据模型-视图-控制器模式，此 URL 段(在index.php之后）表示：

\begin{enumerate}
	\item 第一段表示调用控制器类。
	\item 第二段表示调用类中的函数或方法。
	\item 第三及更多的段表示的是传递给控制器的参数，如 ID 或其它各种变量。
\end{enumerate}

在URL段中，index.php可通过设置 .htaccess 文件来隐藏，使得URL变得更简洁：

\begin{lstlisting}
example.com/class/function/ID
\end{lstlisting}

\subsection{CodeIgnite中的控制器}
控制器是CodeIgnite应用程序的心脏，因为它们决定如何处理 HTTP 请求。一个控制器就是一个类文件，是以一种能够和 URI 关联在一起的方式来命名的。如：

\begin{lstlisting}[caption={URI段中的控制器名称},label=URI]
example.com/index.php/helloworld/
\end{lstlisting}

访问以上URL的话，CodeIgniter 将尝试在application/controllers/子目录中装载一个名为 helloworld.php 的控制器。

我们看一个控制器的例子：
\lstinputlisting{example/PHP/15-ci/application/controllers/helloworld.php}

CodeIgnite框架以灵活著称，强制性的规则较其他框架而言较少，但也有它的一些规定：如对控制器而言，要求类名首字母大写，文件名首字母大写写并默认保存位置在application/controllers/。\footnote{注意，windows下文件名是不区分大小写的，但是Linux是严格区分大小写的。因此，在windows开发环境中，如果不遵守上述规则(即首字母大写，其它字母小写)，程序能正常运行，但如果在Linux环境中，则会出现找不到文件的错误信息。}

上述例子中的Helloworld类继承自CodeIgnite框架的CI\_Controller类，这样就可以使用控制器的一些预设方法。
每个控制器类中的index方法会被认为是默认方法，如当用户访问代码\ref{URI}中的URL时，就会调用helloword控制器的index方法。相当于访问：

\begin{lstlisting}
example.com/index.php/helloworld/index/
\end{lstlisting}

\subsection{CodeIgnite中的视图}
简而言之，一个视图就是一个网页，或是网页的部分，如头部，底部，侧边栏等等。视图必须被一个控制器来调用。
\subsubsection{创建视图}
创建视图实际上就是创建HTML页面的过程，只不过在这个页面中，既可以是纯粹的HTML文件，还可以是嵌入PHP语句的HTML文件。视图文件存放在application/views目录中，如：
\lstinputlisting{example/PHP/15-ci/application/views/helloview.php}

\subsubsection{引用视图}
该视图文件在控制器中的引用方式为：
\lstinputlisting{example/PHP/15-ci/application/controllers/helloview.php}
注意，上述的写法默认引用后缀名为php的文件，如果你的视图文件是.html，那么需要写全文件名。

\subsubsection{视图中的变量}
显然，如果视图中不能使用变量，则视图作为模版就失去了自身的意义。在视图中使用变量的方式和正常PHP程序一样，这也是CodeIgnite框架容易学习的一个因素，即不用学习专门的模板语法，按照原生PHP程序的写法书写模板即可。如下面的例子：
\lstinputlisting{example/PHP/15-ci/application/views/variable.php}

通常，控制器以数组的形式将变量传递至视图 , 这个数组作为视图载入函数的第二个参数。数组中元素的键名分别对应视图中的变量名。如：
\lstinputlisting{example/PHP/15-ci/application/controllers/variable.php}

\subsubsection{在视图中使用循环输出多维数组}
传入视图文件的数据，不仅仅局限于简单的变量。从数据库里面取出数据就是典型的多维数据，这些数据我们事先是不知道它的数量的，因此，之前的变量输出方式就不再适用了。这时候，需要使用循环结构来解决这个问题：
\lstinputlisting[emph={foreach,endforeach,\$userlist},emphstyle=\bfseries]{example/PHP/15-ci/application/views/loopview.php}

对应控制器的代码为：
\lstinputlisting[emph={'userlist'},emphstyle=\bfseries]{example/PHP/15-ci/application/controllers/loop.php}

代码中需要注意的一点是，CodeIgnite框架控制器使用数组键名将变量传递到视图中。

\subsection{CodeIgnite中的模型}
模型是专门用来和数据库打交道的PHP类。模型类文件存放在 application/models/ 文件夹。 如果你愿意，可以在里面建立子文件夹。 类名的首字母必须大写，其他字母小写。 并且确保你的类继承了基本模型类(Base Model Class)。文件名应该是模型类名的小写版。比如：

\lstinputlisting{example/PHP/15-ci/application/models/mvc_model.php}

要使用这个模型，需要在控制器中先载入模型，然后调用模型的适当方法，如：

\lstinputlisting{example/PHP/15-ci/application/controllers/mvc.php}

\subsection{CodeIgnite中的辅助函数}
辅助函数，顾名思义，是帮助我们完成特定任务的函数。每个辅助函数文件仅仅是一些函数的集合。例如，URL Helpers 可以帮助我们创建链接，Form Helpers 可以帮助我们创建表单，Text Helpers 提供一系列的格式化输出方式，Cookie Helpers 能帮助我们设置和读取COOKIE， File Helpers 能帮助我们处理文件，等等。

跟其他部分不同的是，辅助函数不是用类的方式来实现的。它们仅仅是一些简单的过程处理函数。 每个辅助函数处理一个特定的任务，并且不必依靠其他函数。

CodeIgniter 默认是没有载入辅助函数文件的，所以如果你想用辅助函数，就必须先载入它。 一旦被载入，辅助函数将全局可用(globally available)，你可以在 controller 和 views 中使用它们。

辅助函数文件一般保存在 system/helpers 或 application/helpers 文件夹中。CodeIgniter 将会先在 application/helpers 寻找对应的辅助函数文件， 如果目录不存在或者目录下没有对应的辅助函数文件，CI 才会载入 system/helpers 下的辅助函数文件。

\subsubsection{载入辅助函数}
载入辅助函数是非常简单的:
\begin{lstlisting}
$this->load->helper('name');
\end{lstlisting}

name 是辅助函数文件的名字(不带.php后缀 和"helper" 部分)。

例如，要载入文件名为url\_helper.php的URL Helper，你将会用到下面的语句：
\begin{lstlisting}
$this->load->helper('url');
\end{lstlisting}

辅助函数可以在你的控制器(controller)的任何地方被载入。

\subsubsection{URL辅助函数的使用}

\paragraph{site\_url()}

生成指定的完整URL。如果在config.php 文件中指定了base\_url会返回指定的值，否则自动获取。 当你将URI段作为参数传给这个函数时，index.php 文件名 (或者是你在config中自定义的 index\_page 文件名) 会被加到URL后面, 而且会加上你在config中定义的 url\_suffix 后缀。

无论什么时候需要生成 URL ，都鼓励您使用该函数, 这样当您的根 URL 改变的时候更具有可移植性。

作为参数传递给该函数的 URI 段可以是一个字符串。如:
\begin{lstlisting}
echo site_url("news/local/123");
\end{lstlisting}

上面的例子将返回: http://example.com/index.php/news/local/123

\paragraph{base\_url()}

返回在 config.php 中设定的 base\_url. 例:
\begin{lstlisting}
echo base_url();
\end{lstlisting}

这个函数和site\_url返回相同，只是 index\_page 和 url\_suffix 不再被追加。

和site\_url函数一样, 作为参数传递给该函数的 URI 段可以是一个字符串：
\begin{lstlisting}
echo base_url("blog/post/123");
\end{lstlisting}

上面的例子将返回: http://example.com/blog/post/123

这个函数非常有用,不像site\_url()那样，因为不包含index.php这样的路径信息，这就使得可以用它来指定文件的路径，像是image路径或者css路径。

\subsubsection{建立自己的辅助函数}
\label{helper}
可以在你的 application/helpers/ 目录下创建一个新的 helper 辅助函数，文件名为xx\_helper.php。如：

\lstinputlisting{example/PHP/15-ci/application/helpers/tools_helper.php}

自定义辅助函数，当然也需要载入，然后就可以使用辅助函数。
\lstinputlisting{example/PHP/15-ci/application/controllers/tools.php}

\subsubsection{扩展已有的辅助函数}
因为Helper的方法是过程式的(procedural) 和离散(discrete)的，在传统的语言环境中无法被“扩展”，不过在CodeIgniter中，你可以添加或修改helper的方法。 在你的 application/helpers/ 目录下创建一个新的helper，新的helper的名字是在被“扩展”的Helper的名字开头多加一个 MY\_前缀。\footnote{前缀可以在配置文件application/config/config.php中进行修改。}
例如，扩展一个本地已有的 Array Helper 你应该建立一个文件： application/helpers/MY\_array\_helper.php, 并添加或重写（override）其中的一些方法:
\begin{lstlisting}
// any_in_array() is not in the Array Helper, so it defines a new function
function any_in_array( $needle, $haystack ) {
    $needle = ( is_array( $needle ) ) ? $needle : array( $needle );
    foreach ( $needle as $item ) {
        if ( in_array( $item, $haystack ) ) {
            return TRUE;
          }
        }
    return FALSE;
}
// random_element() is included in Array Helper, so it overrides the native function
function random_element( $array ) {
    shuffle( $array );
    return array_pop( $array );
}
\end{lstlisting}
\subsection{CodeIgnite框架中的类库}
所有的类库文件存放在system/libraries 文件夹。大多数情况下你需要预先在controller中初始化后才能使用它们:
\begin{lstlisting}
$this->load->library('class name');
\end{lstlisting}

class name是你想要使用的类名。例如，要载入“表单验证类”，你可以这样做：
\begin{lstlisting}
$this->load->library('form_validation');
\end{lstlisting}
多个类库可以通过传递包含类库的数组一次加载。
\begin{lstlisting}
$this->load->library(array('email', 'table'));
\end{lstlisting}

\subsection{表单验证}

需要注意，表单验证相关的辅助函数只能在表单在正常方式提交时才能正常使用，当使用异步方式提交表单时，无法提示错误信息和回填表单内容。开发人员最好使用基于前端的表单验证。

至此，我们介绍了CodeIgnite框架的基本概念，在之后的章节中，我们将深入学习该框架。

\subsection{程序性能分析及调试} % (fold)
\label{sub:程序性能分析及调试}
可以通过启用codeingite提供的分析器，显示基准测试结果，运行过的 SQL 语句， 以及 \$\_POST 数据。这些信息有助于开发过程中的调试和优化。可在控制器 方法的任何位置添加一行下面的代码:

\begin{lstlisting}
$this->output->enable_profiler(TRUE);
\end{lstlisting}

当启用之后，将会生成一份报告插入到页面的最底部。
% subsection 程序性能分析及调试 (end)

\bibliography{../bib/yangjh}
